#include "pinctrl_led.h"

#ifdef LED_DEBUGB
#define dbg_print(fmt, ...)			\
	do{								\
		printk("LED: ");			\
		printk(fmt, ##__VA_ARGS__);	\
		printk("\r\n");				\
	}while(0)
#else
#define dbg_print(fmt, ...)
#endif

struct led_cdev{
	dev_t dev;
	struct cdev cdev;

	int gpio;
	int cur_state;
};

static int led_open(struct inode *inode, struct file *file);
static int led_release(struct inode *inode, struct file *file);
static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos);
static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos);

static struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.read = led_read,
	.write = led_write,
};
static struct class *led_cls;


static int led_open(struct inode *inode, struct file *file)
{
	struct led_cdev *pled_cdev = container_of(inode->i_cdev, struct led_cdev, cdev);
	file->private_data = pled_cdev;
	return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos)
{
	char kernal_data;
	struct led_cdev *pled_cdev = file->private_data;

	if(pled_cdev->cur_state == 0)
		kernal_data = '0';
	else if(pled_cdev->cur_state == 1)
		kernal_data = '1';
	else
		return -EINVAL;

	//将数据从内核空间拷贝到用户空间
	if(copy_to_user(buf, &kernal_data, 1))
		return -EFAULT;

	return 1;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos)
{
	char user_data;
	struct led_cdev *pled_cdev = file->private_data;

	//将数据从用户框架拷贝到内核框架
	if(copy_from_user(&user_data, buf, 1))
		return -EFAULT;

	if(user_data== '0')
	{
		pled_cdev->cur_state = 0;
		gpio_set_value(pled_cdev->gpio, 1);
	}
	else if(user_data == '1')
	{
		pled_cdev->cur_state = 1;
		gpio_set_value(pled_cdev->gpio, 0);
	}
	else
		return -EINVAL;

	return len;
}

static int register_led(struct platform_device *pled_dev, struct led_cdev *pled_cdev)
{
	int result;
	struct device *device;

#ifndef USING_ALLOC_DEV_ID
	//注册设备ID
	pled_cdev->dev = MKDEV(LED_MAJOR, pled_dev->id);
	result = register_chrdev_region(pled_cdev->dev, 1, LED_NAME);
	if(result != 0)
		return result;
#else
	//动态分配设备ID
	result = alloc_chrdev_region(&pled_cdev->dev, pled_dev->id, 1, LED_NAME);
	if(result != 0)
		return result;
#endif

	//初始化CDEV对象
	cdev_init(&pled_cdev->cdev, &led_ops);
	pled_cdev->cdev.owner = THIS_MODULE;

	//添加cdev对象到系统
	result = cdev_add(&pled_cdev->cdev, pled_cdev->dev, 1);
	if(result != 0)
	{
		unregister_chrdev_region(pled_cdev->dev, 1);
		return result;
	}

	//自动创建设备文件
	device = device_create(led_cls, NULL, pled_cdev->dev, NULL, "myled%d", pled_dev->id);
	if(IS_ERR(device))
	{
		cdev_del(&pled_cdev->cdev);
		unregister_chrdev_region(pled_cdev->dev, 1);
		return PTR_ERR(device);
	}
	return 0;
}

static void unregister_led(struct led_cdev *pled_cdev)
{
	device_destroy(led_cls, pled_cdev->dev);
	cdev_del(&pled_cdev->cdev);
	unregister_chrdev_region(pled_cdev->dev, 1);
}

static int pled_probe(struct platform_device *pled_dev)
{
	int result;
	struct led_cdev *pled_cdev;

	printk("**%s**\r\n", pled_dev->name);

	//获取设备ID号
	result = of_property_read_u32(pled_dev->dev.of_node, "id", &pled_dev->id);
	if(result != 0)
	{
		dbg_print("get dev id failed\r\n");
		return result;
	}

	//分配LED设备
	pled_cdev = kmalloc(sizeof(struct led_cdev), GFP_KERNEL);
	if(!pled_cdev)
	{
		dbg_print("malloc mem failed\r\n");
		return -ENOMEM;
	}
	memset(pled_cdev, 0, sizeof(struct led_cdev));

	//在设备中设置驱动私有数据
	pled_dev->dev.driver_data = pled_cdev;

	//获取GPIO号
	pled_cdev->gpio = of_get_named_gpio(pled_dev->dev.of_node, "led-gpio", 0);
	if(pled_cdev->gpio < 0)
	{
		dbg_print("get gpio failed\r\n");
		kfree(pled_cdev);
		return pled_cdev->gpio;
	}

	//申请GPIO
	result = gpio_request(pled_cdev->gpio, "key");
	if(result != 0)
	{
		dbg_print("request gpio failed\r\n");
		kfree(pled_cdev);
		return result;
	}

	//注册led字符设备
	result = register_led(pled_dev, pled_cdev);
	if(result != 0)
	{
		dbg_print("register led failed\r\n");
		gpio_free(pled_cdev->gpio);
		kfree(pled_cdev);
		return result;
	}

	//设置GPIO为输出并点亮led
	gpio_direction_output(pled_cdev->gpio, 0);
	pled_cdev->cur_state = LED_ON;

	return 0;
}

static int pled_remove(struct platform_device *pled_dev)
{
	struct led_cdev *pled_cdev;

	printk("**%s**\r\n", pled_dev->name);
	
	//获取设备中的驱动私有数据
	pled_cdev = pled_dev->dev.driver_data;
	//关闭led
	gpio_set_value(pled_cdev->gpio, 1);
	//注销LED
	unregister_led(pled_cdev);
	//释放GPIO
	gpio_free(pled_cdev->gpio);
	//释放内存
	kfree(pled_cdev);

	return 0;
}

/* 匹配列表，用于设备树和驱动进行匹配 */
static struct of_device_id led_of_match[] = {
	{.compatible = "platform_led"},
	{},
};

static struct platform_driver pled_drv = { 
	.driver = {
		.name = "platform_led",
		.of_match_table = led_of_match,
	},
	.probe = pled_probe,
	.remove = pled_remove,
};


#if 1

static int __init pled_init(void)
{
	int result;

	led_cls = class_create(THIS_MODULE, LED_NAME);
	if(IS_ERR(led_cls))
	{
		dbg_print("create led class failed\r\n");
		return PTR_ERR(led_cls);
	}
	result = platform_driver_register(&pled_drv);
	if(result != 0)
	{
		dbg_print("register platform driver failed\r\n");
		return result;
	}
	return result;
}

static void __exit pled_exit(void)
{
	class_destroy(led_cls);
	platform_driver_unregister(&pled_drv);
}

module_init(pled_init);
module_exit(pled_exit);

#else

module_platform_driver(pled_drv);

#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("pinctrl test");
MODULE_ALIAS("pinctrl_led");
